#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Created on 2009-11-18.
# $Id$
#

import datetime
import hashlib
import logging
import os
import re
import urllib

from urlparse import urlparse
try:
    from urlparse import parse_qs
except ImportError:
    from cgi import parse_qs  # Python 2.5 fallback.

from django import template
from django.utils.encoding import force_unicode
from django.utils.safestring import mark_safe


register = template.Library()


#---------------------------------------------------------------------------------------------------
# simple extra filters
#---------------------------------------------------------------------------------------------------


@register.filter
def uniqueid(value):
    return "%x" % id(value)


@register.filter
def multiply(value, arg):
    """
    Multiplies the value (as an integer) by arg.
    """
    if isinstance(value, int) or isinstance(value, float):
        return value * arg
    return u"%s x %s" % (value, arg)


@register.filter
def modulo(value, arg):
    """
    Returns value mod arg.
    """
    if isinstance(value, int) and isinstance(arg, int) and arg != 0:
        return value % arg
    return u"%s mod %s" % (value, arg)


@register.filter
def summary(value, arg):
    value = force_unicode(value)
    if len(value) > arg:
        return u"%s..." % value[:arg]
    else:
        return value


@register.filter
def prettify_datetime(value):
    """
    Prettifies a date or a datetime, returns a humain read-able string.
    """

    # Check the value argument type, and calculate dt.
    if isinstance(value, datetime.datetime):
        dt = datetime.datetime.now() - value
    elif isinstance(value, datetime.date):
        dt = datetime.date.today() - value
    else:
        return value

    # Check if value is in the future or in the past, and abs(dt).
    is_past = (dt >= datetime.timedelta(0))
    dt = abs(dt)

    # Generate the basic pretty string for value.
    time_units = (
        ("year", dt.days // 365),
        ("month", (dt.days % 365) // 30),
        ("day", dt.days % 30),
        ("hour", dt.seconds // 3600),
        ("minute", (dt.seconds % 3600) // 60),
    )
    pretty_str = None
    for unit, amount in time_units:
        if amount > 0:
            pretty_str = "%d %s" % (amount, unit)
            if amount > 1:
                pretty_str += "s"
            break

    # Post-process the basic pretty string.
    if is_past:
        if not pretty_str:
            pretty_str = "just now"
        else:
            pretty_str += " ago"
    else:
        if not pretty_str:
            pretty_str = "right now"
        else:
            pretty_str = "in " + pretty_str

    # Return the final result.
    return pretty_str


@register.filter
def safe_email(value):
    if not isinstance(value, basestring) or len(value) <= 4:
        return value
    return u"%s..." % value[:-4].replace("@", " [at] ")


@register.filter
def gravatar(value):
    if not isinstance(value, basestring):
        return value
    else:
        email_md5 = hashlib.md5(value.lower()).hexdigest().lower()
        gravatar_url = "http://www.gravatar.com/avatar/%s" % email_md5
        gravatar_url += "?" + urllib.urlencode({"s": 48, "d": "wavatar"})
        return gravatar_url


#---------------------------------------------------------------------------------------------------
# smart_escape
#---------------------------------------------------------------------------------------------------


class SmartLink(object):
    """
    This class wraps a URL and renders the URL to HTML code. By default, a URL will be rendered as
    an HTML anchor element (``a``). But different URLs may result in different HTML code.
    """

    def __init__(self, url):
        self.url = url

    def as_html(self):
        # Parse the URL and its query string.
        parsed = urlparse(self.url)
        params = parse_qs(parsed.query)
        # If the scheme is not 'http', return an ``a`` element.
        if parsed.scheme != "http":
            return self._as_anchor(self.url)
        # If the URL links to a Youtube video, return an embeded video.
        if parsed.netloc == "www.youtube.com" and parsed.path == "/watch" and params.get("v"):
            return self._as_youtube(params["v"][0])
        # If the URL links to an image, return an ``img`` element.
        path_ext = os.path.splitext(parsed.path)[1].lower()
        if path_ext in (".jpg", ".gif", ".png") and not params:
            return self._as_img(self.url)
        # By default, return an ``a`` element.
        return self._as_anchor(self.url)

    def _as_anchor(self, url):
        return u" <a href='%s'>%s</a> " % (url, url)

    def _as_youtube(self, video):
        data = {
            "embed_url": u"http://www.youtube.com/v/%s&amp;hl=en_US&amp;fs=1?border=1" % video,
            "width": 500,
            "height": 405,
        }
        embed_code = u"\n\n" \
            u"<object width='%(width)s' height='%(height)s'>" \
            u"<param name='movie' value='%(embed_url)s'></param>" \
            u"<param name='allowFullScreen' value='true'></param>" \
            u"<param name='allowscriptaccess' value='always'></param>" \
            u"<embed src='%(embed_url)s' type='application/x-shockwave-flash'" \
            u"       allowscriptaccess='always' allowfullscreen='true'" \
            u"       width='%(width)s' height='%(height)s'>" \
            u"</embed>" \
            u"</object>" \
            u"\n\n" % data
        return embed_code

    def _as_img(self, url):
        data = {"url": url}
        img_code = u"\n\n" \
            u"<a href='%(url)s'>" \
            u"<img style='max-width: 500px; border: 1px solid #BBBBBB;'" \
            u"     src='%(url)s' alt='external image' />" \
            u"</a>" \
            u"\n\n" % data
        return img_code


def _render_smart_links(value):
    pattern = [
        r"(http[s]?|ftp|mailto)://",     # schema
        r"[\w\-\.]+\.[a-zA-Z]+(:\d+)?",  # netloc
        r"(/[\w\-\.\+\$/~!:;#%]*)?",     # path starting with '/' (optional)
        r"(\?[\w\-\.\+\$/~!:;#%=&]*)?",  # query string starting with '?' (optional)
    ]
    exp = re.compile("".join(pattern))
    return exp.sub(lambda m: SmartLink(m.group(0)).as_html(), value)


def _render_markups(value):
    exp = re.compile(r"(?P<markup>==|\*\*|\*|``)(?P<text>.+)(?P=markup)")
    def transform(match):
        MARKUP_TO_TAG = {"==": "h2", "**": "b", "*": "i", "``": "code"}
        tag = MARKUP_TO_TAG.get(match.group("markup"), "span")
        return u"<%s>%s</%s>" % (tag, match.group("text"), tag)
    return exp.sub(transform, value)


@register.filter
def smart_escape(value):
    value = force_unicode(value).replace("&", "&amp;") \
            .replace("<", "&lt;").replace(">", "&gt;") \
            .replace('"', "&quot;").replace("'", "&#39;")
    try:
        value = _render_smart_links(value)
        value = _render_markups(value)
        return mark_safe(value)
    except Exception, exc:
        message = "Fail to perform smart escape: %s" % exc
        logging.error(message)
        logging.exception(exc)
        return mark_safe(escaped)


# EOF
